package com.atguigu.gulimall.order.service.impl;

import com.alibaba.fastjson.TypeReference;
import com.atguigu.common.exception.NoStockException;
import com.atguigu.common.parameters.dto.OrderItemDto;
import com.atguigu.common.parameters.dto.WareSkuLockDto;
import com.atguigu.common.to.SkuHasStockVo;
import com.atguigu.common.utils.PageUtils;
import com.atguigu.common.utils.Query;
import com.atguigu.common.utils.R;
import com.atguigu.common.vo.FareVo;
import com.atguigu.common.vo.MemberAddressVo;
import com.atguigu.common.vo.MemberVo;
import com.atguigu.common.vo.OrderSpuInfoVo;
import com.atguigu.gulimall.order.constant.OrderConstant;
import com.atguigu.gulimall.order.constant.OrderStatusEnum;
import com.atguigu.gulimall.order.dao.OrderDao;
import com.atguigu.gulimall.order.entity.OrderEntity;
import com.atguigu.gulimall.order.entity.OrderItemEntity;
import com.atguigu.gulimall.order.feign.CartFeignService;
import com.atguigu.gulimall.order.feign.MemberFeignService;
import com.atguigu.gulimall.order.feign.ProductFeignService;
import com.atguigu.gulimall.order.feign.WareFeignService;
import com.atguigu.gulimall.order.interceptor.LoginUserInterceptor;
import com.atguigu.gulimall.order.service.OrderItemService;
import com.atguigu.gulimall.order.service.OrderService;
import com.atguigu.gulimall.order.to.OrderCreateTo;
import com.atguigu.gulimall.order.vo.OrderConfirmVo;
import com.atguigu.gulimall.order.vo.OrderSubmitVo;
import com.atguigu.gulimall.order.vo.SubmitOrderRespVo;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import io.seata.spring.annotation.GlobalTransactional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;

import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;


@Service("orderService")
public class OrderServiceImpl extends ServiceImpl<OrderDao, OrderEntity> implements OrderService {

    private ThreadLocal<OrderSubmitVo> submitVoThreadLocal = new ThreadLocal<>();

    @Autowired
    private MemberFeignService memberFeignService;

    @Autowired
    private CartFeignService certFeignService;

    @Autowired
    private ThreadPoolExecutor threadPoolExecutor;

    @Autowired
    private WareFeignService wareFeignService;

    @Autowired
    private StringRedisTemplate redisTemplate;

    @Autowired
    private ProductFeignService productFeignService;

    @Autowired
    private OrderItemService orderItemService;

    @Override
    public PageUtils queryPage(Map<String, Object> params) {
        IPage<OrderEntity> page = this.page(
                new Query<OrderEntity>().getPage(params),
                new QueryWrapper<OrderEntity>()
        );

        return new PageUtils(page);
    }

    @Override
    public OrderConfirmVo confirmOrder() {
        OrderConfirmVo confirmVo = new OrderConfirmVo();
        MemberVo memberVo = LoginUserInterceptor.loginUser.get();
        // 1、远程查询所有的收货地址列表
        CompletableFuture<Void> getAddressList = CompletableFuture.runAsync(() -> {
            R addressResult = memberFeignService.getAddressList(memberVo.getId());
            if (!ObjectUtils.isEmpty(addressResult)) {
                List<MemberAddressVo> addressList = addressResult.getData("addressList", new TypeReference<List<MemberAddressVo>>() {
                });
                confirmVo.setAddress(addressList);
            }
        }, threadPoolExecutor);
        // 2、远程查询所有的订单商品信息
        CompletableFuture<Void> getOrderItems = CompletableFuture.runAsync(() -> {
            R itemsResult = certFeignService.getOrderItems(memberVo.getId());
            if (!ObjectUtils.isEmpty(itemsResult)) {
                List<OrderItemDto> orderItems = itemsResult.getData("orderItems", new TypeReference<List<OrderItemDto>>() {
                });
                // 计算商品总数量和订单总额
                for (OrderItemDto item : orderItems) {
                    Integer totalCount = confirmVo.getTotalCount();
                    totalCount += item.getCount();
                    confirmVo.setTotalCount(totalCount);
                    BigDecimal total = confirmVo.getTotalPrice();
                    confirmVo.setTotalPrice(total.add(item.getTotalPrice()));
                }
                confirmVo.setOrderItemVos(orderItems);
            }
        }, threadPoolExecutor).thenRunAsync(() -> {
            // 查询订单项的库存信息
            List<OrderItemDto> orderItemVos = confirmVo.getOrderItemVos();
            List<Long> skuIds = orderItemVos.stream().map(item -> item.getSkuId()).collect(Collectors.toList());
            R wareResult = wareFeignService.getSkusHasStock(skuIds);
            if (!ObjectUtils.isEmpty(wareResult)) {
                List<SkuHasStockVo> skuHasStockVos = wareResult.getData("data", new TypeReference<List<SkuHasStockVo>>() {
                });
                Map<Long, Boolean> map = skuHasStockVos.stream().collect(
                        Collectors.toMap(SkuHasStockVo::getSkuId, SkuHasStockVo::getHasStock));
                confirmVo.setStocks(map);
            }
        });
        // 3、计算付款价格
        try {
            CompletableFuture.allOf(getAddressList, getOrderItems).get();
        } catch (Exception e) {
            e.printStackTrace();
        }
        if (memberVo.getIntegration() != null) {
            BigDecimal disCount = BigDecimal.valueOf(memberVo.getIntegration() * 0.1);
            confirmVo.setDisCount(disCount);
            BigDecimal payPrice = confirmVo.getTotalPrice().subtract(disCount);
            confirmVo.setPayPrice(payPrice);
        }
        // 4、生成防重令牌
        String orderToken = UUID.randomUUID().toString().replace("-", "");
        confirmVo.setOrderToken(orderToken);
        redisTemplate.opsForValue().set(OrderConstant.USER_ORDER_TOKEN + memberVo.getId(), orderToken, 30, TimeUnit.MINUTES);
        return confirmVo;
    }

    @GlobalTransactional
    @Transactional
    @Override
    public SubmitOrderRespVo submitOrder(OrderSubmitVo vo) {
        SubmitOrderRespVo respVo = new SubmitOrderRespVo();
        MemberVo memberVo = LoginUserInterceptor.loginUser.get();
        submitVoThreadLocal.set(vo);
        // 验证防重令牌（原子性）
        String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
        Long result = redisTemplate.execute(new DefaultRedisScript<Long>(script, Long.class),
                                            Arrays.asList(OrderConstant.USER_ORDER_TOKEN + memberVo.getId()),
                                            vo.getOrderToken());
        if (result != 0l) {
            // 创建订单
            OrderCreateTo createTo = createOrder();
            // 验价
            BigDecimal payPrice = createTo.getPayPrice();
            BigDecimal voPayPrice = vo.getPayPrice();
            if (Math.abs(payPrice.subtract(voPayPrice).doubleValue()) < 0.01) {
                // 保存订单
                saveOrder(createTo);
                // 库存锁定，只要出现异常，就回滚订单数据
                WareSkuLockDto wareSkuLockDto = new WareSkuLockDto();
                wareSkuLockDto.setOrderSn(createTo.getOrder().getOrderSn());
                List<OrderItemDto> collect = createTo.getOrderItems().stream().map(item -> {
                    OrderItemDto itemDto = new OrderItemDto();
                    itemDto.setSkuId(item.getSkuId());
                    itemDto.setCount(item.getSkuQuantity());
                    itemDto.setTitle(item.getSkuName());
                    return itemDto;
                }).collect(Collectors.toList());
                wareSkuLockDto.setLock(collect);
                R lockResult = wareFeignService.orderLockStock(wareSkuLockDto);
                if (!ObjectUtils.isEmpty(lockResult) && lockResult.getCode() == 0) {
                    respVo.setOrder(createTo.getOrder());
                    respVo.setCode(OrderConstant.ORDER_SUBMIT_SUCCESS);
                    return respVo;
                } else {
                    respVo.setCode(OrderConstant.ORDER_WARE_STOCK_LOCK_FAIL);
                    throw new NoStockException();
                }
            } else {
                respVo.setCode(OrderConstant.ORDER_PRICE_CHECK_FAIL);
            }
        } else {
            respVo.setCode(OrderConstant.ORDER_TOKEN_CHECK_FAIL);
        }
        return respVo;
    }

    /**
     * 保存订单内容
     * @param createTo
     * @return
     */
    private void saveOrder(OrderCreateTo createTo) {
        this.save(createTo.getOrder());
        List<OrderItemEntity> orderItems = createTo.getOrderItems();
        orderItemService.insertBatch(orderItems);
    }

    /**
     * 根据订单提交的信息，去生成订单
     *
     * @return
     */
    private OrderCreateTo createOrder() {
        OrderCreateTo orderTo = new OrderCreateTo();
        OrderEntity orderEntity = new OrderEntity();
        // 订单号
        String orderSn = IdWorker.getTimeId();
        orderEntity.setOrderSn(orderSn);
        // 收货人信息和运费
        OrderSubmitVo submitVo = submitVoThreadLocal.get();
        R fareResult = wareFeignService.getFare(submitVo.getAddrId());
        FareVo fareVo = fareResult.getData("data", new TypeReference<FareVo>() {
        });
        orderEntity.setFreightAmount(fareVo.getFare());
        orderEntity.setReceiverCity(fareVo.getAddress().getCity());
        orderEntity.setReceiverDetailAddress(fareVo.getAddress().getDetailAddress());
        orderEntity.setReceiverName(fareVo.getAddress().getName());
        orderEntity.setReceiverPhone(fareVo.getAddress().getPhone());
        orderEntity.setReceiverPostCode(fareVo.getAddress().getPostCode());
        orderEntity.setReceiverProvince(fareVo.getAddress().getProvince());
        orderEntity.setReceiverRegion(fareVo.getAddress().getRegion());
        // 订单项信息
        MemberVo memberVo = LoginUserInterceptor.loginUser.get();
        orderEntity.setMemberId(memberVo.getId());
        orderEntity.setMemberUsername(memberVo.getUsername());
        R itemsResult = certFeignService.getOrderItems(memberVo.getId());
        if (!ObjectUtils.isEmpty(itemsResult)) {
            List<OrderItemDto> orderItems = itemsResult.getData("orderItems", new TypeReference<List<OrderItemDto>>() {
            });
            if (!CollectionUtils.isEmpty(orderItems)) {
                List<OrderItemEntity> collect = orderItems.stream().map(cartItem -> {
                    OrderItemEntity orderItemEntity = new OrderItemEntity();
                    orderItemEntity.setOrderSn(orderSn);
                    orderItemEntity.setRealAmount(cartItem.getTotalPrice());
                    // 商品sku信息
                    orderItemEntity.setSkuId(cartItem.getSkuId());
                    orderItemEntity.setSkuName(cartItem.getTitle());
                    orderItemEntity.setSkuPic(cartItem.getImage());
                    orderItemEntity.setSkuPrice(cartItem.getPrice());
                    String skuAttr = StringUtils.collectionToDelimitedString(cartItem.getSkuAttr(), ";");
                    orderItemEntity.setSkuAttrsVals(skuAttr);
                    orderItemEntity.setSkuQuantity(cartItem.getCount());
                    // 积分
                    orderItemEntity.setGiftGrowth(cartItem.getTotalPrice().intValue());
                    orderItemEntity.setGiftIntegration(cartItem.getTotalPrice().intValue());
                    // 商品spu信息
                    R spuResult = productFeignService.getSpuInfoBySkuId(cartItem.getSkuId());
                    if (!ObjectUtils.isEmpty(spuResult)) {
                        OrderSpuInfoVo spuInfoVo = spuResult.getData("data", new TypeReference<OrderSpuInfoVo>() {
                        });
                        orderItemEntity.setSpuId(spuInfoVo.getSpuId());
                        orderItemEntity.setSpuName(spuInfoVo.getSpuName());
                        orderItemEntity.setSpuBrand(spuInfoVo.getSpuBrand());
                        orderItemEntity.setCategoryId(spuInfoVo.getCategoryId());
                        orderItemEntity.setSkuPic(spuInfoVo.getSpuPic());
                    }
                    return orderItemEntity;
                }).collect(Collectors.toList());
                orderTo.setOrderItems(collect);
            }
            // 订单金额
            BigDecimal total = new BigDecimal("0.0");
            BigDecimal gift = new BigDecimal("0.0");
            BigDecimal growth = new BigDecimal("0.0");
            for (OrderItemEntity item : orderTo.getOrderItems()) {
                total = total.add(item.getRealAmount());
                gift = gift.add(new BigDecimal(item.getGiftIntegration().toString()));
                growth = growth.add(new BigDecimal(item.getGiftGrowth().toString()));
            }
            orderEntity.setIntegration(gift.intValue());
            orderEntity.setGrowth(growth.intValue());
            orderEntity.setTotalAmount(total);
            BigDecimal disCount = BigDecimal.valueOf(memberVo.getIntegration() * 0.1);
            BigDecimal newPayPrice = total.subtract(disCount).add(orderEntity.getFreightAmount());
            orderEntity.setPayAmount(newPayPrice);
            orderTo.setPayPrice(newPayPrice);
            // 订单状态信息
            orderEntity.setStatus(OrderStatusEnum.CREATE_NEW.getCode());
            orderEntity.setAutoConfirmDay(7);
            orderEntity.setDeleteStatus(0);
            orderEntity.setModifyTime(new Date());
            orderEntity.setCreateTime(new Date());
        }
        orderTo.setOrder(orderEntity);
        return orderTo;
    }
}